16 混淆指针与数组的问题
下面的程序输出什么?
//define.c
#include <stdio.h>
char g_name[] = "Hello World!";
void define_print(){
printf("define_print():%s\n",g_name);
}
//main.c
#include <stdio.h>
extern char* g_name;
int main(){
define_print();
printf("main():%s\n",g_name);
return 0;
}
实例分析:指针==数组?
//define.c
#include <stdio.h>
char g_name[] = "Hello World!";
void define_print()
{
printf("define_print() : %s\n", g_name);
}
//main.c
#include <stdio.h>
extern char* g_name;
int main()
{
define_print();
printf("main() : %s\n", g_name);
return 0;
}
再论指针和数组
-
指针
- 本质为变量,保存的目标值为内存地址
- 指针运算与*操作符配合使用能够模拟数组行为
-
数组
- 数组是一段连续的存储空间
- 数组名可看做指向数组第一个元素的常量指针
-
在C语言层面中的等价关系
int a[3] = {0};
int *p = a;
p[0] = 1; //a[0]=1
p[1] = 2; //a[1]=2
a[2] = 3; //p[2]=3 -
问题
在二进制层面,指针和数组是否等价?
编程实验:数组与指针深度分析
#include <stdio.h>
int test()
{
int a[3] = {0};
int* p = a;
p[0] = 1; // a[0] = 1
p[1] = 2; // a[1] = 2
a[2] = 3; // p[2] = 3
}
int main()
{
test();
return 0;
}
-
结论
- 指针与数组在二进制层面的操作完全不同
- 指针操作:先寻址,再对地址单元进行操作
- 数组操作:直接针对地址单元进行操作
-
C/C++编译器的天生缺陷
- 由4个子部件组成(预处理器,编译器,汇编器,链接器)
- 每个子部件独立工作,相互之间没有通信
- 语法规范只在编译阶段有效(如:类型约束,保护成员)
- 编译器部件对各个源文件进行独立编译(认为源文件相互独立)
-
问题本质
编程实验:本质分析
- 解决方案
- 尽可能不使用跨文件的全局变量(非static全局变量)
- 当必须使用时,在统一固定的头文件中声明(global.h)
- 使用跨文件全局变量的源文件直接包含global.h
- 定义跨文件全局变量的源文件也需要包含global.h
- 小贴士:声明和定义不同
- 声明只是告诉编译器,目标存在,可使用
- 定义实际为目标分配内存(变量)或确定执行流(函数)
- 理论上,任何目标都需要先声明,再使用
- C/C++语言允许声明与定义的统一
编程实验:解决方案
小结
- C/C++语言中的指针与数组在部分情况下可等价
- 指针与数组在二进制层面的操作完全不同
- C/C++编译器忽略了源码间的依赖关系
- 在统一固定的头文件中声明跨文件使用的全局变量(global.h)
- 使用跨文件全局变量的源文件直接包含global.h